package com.xl.utils.excel.util;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.annotation.ExcelProperty;
import com.xl.annotation.ExcelPropertyCheck;
import com.xl.exception.GlobalException;
import com.xl.utils.excel.config.ExcelReadListenerConfig;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Pattern;

import static jdk.nashorn.api.scripting.ScriptUtils.convert;

/**
 * @Author:XueH
 * @Description:
 * @Date:Created in 16:40 2023/6/5
 * @Modified By:
 */
public class EasyExcelUtils {

    public static <T> List<T> readExcel(MultipartFile file, Class<T> target, boolean checkHead) {
        ExcelReadListenerConfig excelReadListener = new ExcelReadListenerConfig();
        try {
            EasyExcelFactory.read(file.getInputStream(), excelReadListener).sheet().doRead();
        } catch (IOException e) {
            e.printStackTrace();
        }
        List<Map<Integer, String>> headList = excelReadListener.getHeadList();
        if (CollectionUtils.isEmpty(headList)) {
            throw new GlobalException("表格中未包含表头信息!");
        }
        if (checkHead) {
            Map<Integer, String> map = headList.stream().findFirst().get();
            Collection<String> values = map.values();

            List<String> targetHeads = null;
            try {
                targetHeads = getTargetHeads(target);
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            if (values.size() != targetHeads.size() || !(values.containsAll(targetHeads) && targetHeads.containsAll(values))) {
                throw new GlobalException("请传入指定模板文件!");
            }
        }

        List<Map<Integer, String>> dataList = excelReadListener.getDataList();
        if (CollectionUtils.isEmpty(dataList)) {
            throw new GlobalException("请不要传入空白模板,请调整后上传!");
        }
        //封装数据体
        List<Map<String, Object>> excelDataList = new ArrayList<>();
        for (Map<Integer, String> headMap : headList) {
            for (Map<Integer, String> dataRow : dataList) {
                Map<String, Object> rowData = new LinkedHashMap<>();
                headMap.forEach((key, value) -> rowData.put(value, dataRow.get(key)));
                excelDataList.add(rowData);
            }
        }
        //字段转驼峰
        return convertList(excelDataList, target);
    }

    private static <T> List<String> getTargetHeads(Class<T> target) throws Exception {
        ArrayList<String> heads = new ArrayList<>();

        T t = target.newInstance();
        Field[] fields = t.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (!"serialVersionUID".equals(field.getName())) {
                ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
                if (Objects.isNull(annotation)) {
                    continue;
                }
                String[] valueNames = annotation.value();

                if (ArrayUtil.isEmpty(valueNames)) {
                    continue;
                }
                String valueName = valueNames[0];
                heads.add(valueName);
            }
        }
        return heads;
    }

    private static <T> List<T> convertList(List<Map<String, Object>> excelDataList, Class<T> clazz) {
        List<T> ts = new ArrayList<>();
        excelDataList.forEach(data -> {
            try {
                T t = clazz.newInstance();
                Field[] fields = t.getClass().getDeclaredFields();
                for (Field field : fields) {
                    if (!"serialVersionUID".equals(field.getName())) {
                        ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
                        if (Objects.isNull(annotation)) {
                            continue;
                        }
                        String[] valueNames = annotation.value();

                        if (ArrayUtil.isEmpty(valueNames)) {
                            continue;
                        }
                        String valueName = valueNames[0];
                        field.setAccessible(true);
                        Object value = data.get(valueName);
                        //读取到的Excel中的数据为Object类型，这里转为实体类指定的属性 在这里可能就会转换失败，这里转换失败不会报错，而是给一个默认值
                        field.set(t, convert(value, field.getType()));

                        //检查各个字段是否为空  格式是否正确  长度是否正确
                        ExcelPropertyCheck excelPropertyCheck = field.getAnnotation(ExcelPropertyCheck.class);
                        //是否对字段进行检查
                        if (ObjUtil.isNotNull(excelPropertyCheck)) {
                            //非空检查
                            if (excelPropertyCheck.checkBlank()) {
                                if (ObjectUtil.isNull(value))
                                    throw new GlobalException(excelPropertyCheck.propertyName() + excelPropertyCheck.blankMsg());
                            }
                            //长度检查
                            if (excelPropertyCheck.checkLength() != -1) {
                                if (value.toString().trim().length() != excelPropertyCheck.checkLength())
                                    throw new GlobalException(excelPropertyCheck.propertyName() + excelPropertyCheck.lengthMsg() + "长度应为：" + excelPropertyCheck.checkLength());
                            }
                            //格式检查
                            if (!Pattern.compile(excelPropertyCheck.checkRegex()).matcher(value.toString()).matches()) {
                                throw new GlobalException(excelPropertyCheck.propertyName() + excelPropertyCheck.regexMsg());
                            }
                        }
                    }
                }
                ts.add(t);
            } catch (Exception e) {
                boolean equals = GlobalException.class.equals(e.getClass());
                if (equals) {
                    throw new GlobalException(e.getMessage());
                }
            }
        });
        return ts;
    }


}
